--代替Lua本身的module, require机制
_G.MODULE_LOAD_DATA = _G.MODULE_LOAD_DATA or {}
--模块的加载顺序
_G.MODULE_LOAD_LIST = _G.MODULE_LOAD_LIST or {}


ALL_CLASS = ALL_CLASS or {}	-- 全局变量，记录所有 Class 与 Class:GetType() 对应关系
ALL_CLASS_PATH = ALL_CLASS_PATH or {}
setmetatable(ALL_CLASS, {__mode = "kv"})

-- dest old, src new
local function ReplaceTbl(dest, src, ignoreVar)
	local function RealFun(dest, src, depth, ignoreVar)
		assert(type(dest)=="table" and type(src)=="table", "error data type")
		if not depth then
			depth = 0
		end
		if depth >= 20 then
			error("too long Depth to replace")
			return
		end
		for k, v in pairs(dest) do
			-- 就第一层判断忽略更新
			if k ~= "__ignore_update_var" and (not ignoreVar or not ignoreVar[k]) then 
				if type(v) == "table" then
					-- 不对__index做deep替换
					if type(src[k]) == "table" and k ~="__index" then
						RealFun(v, src[k], depth+1)
					else
						dest[k] = src[k]
					end
				else
					-- todo: 如果需要删除属性，可以直接使用热修改的方式
					if src[k] then
						dest[k] = src[k]   --删除func这里赋值nil
					end
				end
			end
		end
		--把新增的数据或者方法引用进来
		for k, v in pairs(src) do
			--注意一定要使用rawget，否则在以下情况更新会失败.
			--父类A实现了test函数，子类A1未实现，然后在线添加A1.test的实现并更新。
			--这时候Dest[k]实际上访问了metatable中父类的实现，所以不为nil，于是更新失败
			if rawget(dest, k) == nil then
				dest[k] = v  --新增func 这里赋值
			end
		end
		--这里将新的元表替换旧的
		setmetatable(dest, getmetatable(src))
	end
	RealFun(dest, src, nil, ignoreVar)
end

--不抛出错误的Import

function ModuleToString(m)
    return string.format("<<MODULE-%s>>", m.__name__ or "nil")
end

local function CallInit(mod, pathFile)
    -- 注册所有的 Class 到 ALL_CLASS 里去，依赖 Class:GetType() 属性，注意：只遍历 1 层
    for k, v in pairs(mod) do
        if type(v) == "table" and rawget(v, "__ClassType") then
            local classType = v:GetType()
			local oldClass = ALL_CLASS[classType]
		local oldPath = ALL_CLASS_PATH[classType]
			-- 如果类已经注册，并且声明文件不同，类对象不同，就提示
            if (oldPath and oldPath ~= pathFile) and (oldClass and oldClass ~= v) then
                print("class type repeat regiester:", classType, oldPath, pathFile)
            else
                ALL_CLASS_PATH[classType] = pathFile
            end
            ALL_CLASS[classType] = v
        end
    end
    
    -- 载入模块时调用其构造函数
    mod.__name__ = pathFile
	if mod.__init__ then
		local ok,msg = xpcall(function() mod:__init__() end)
    end
end

local function CallDestroy(mod)
    if mod.__destroy__ then
		xpcall(function() mod:__destroy__() end, debug.excepthook)
    end
end

local function ImportNewModule(pathFile)
	local mod = {__name__ = pathFile}
	MODULE_LOAD_DATA[pathFile] = mod
	setmetatable(mod, {__index = _G, __tostring = ModuleToString})
	local chunk, err = loadfile(pathFile, nil, mod)
	if not chunk then
		return chunk, err
	end

	chunk()
	mod.__status__ = "import"
	CallInit(mod, pathFile)
	table.insert(MODULE_LOAD_LIST, mod)
	return mod
end

local function SafeImport(filePath)
	if filePath[0] == '.' or filePath[0] == '/' then
		return nil, "forbidden import"
	end
	if string.sub(filePath, -4) ~= ".lua" then
		filePath = filePath .. ".lua"
	end
	if #filePath > 59 then
		_WARN("[SafeImport] path too long, %s", filePath)
	end

	local mod = MODULE_LOAD_DATA[filePath]
	if not mod then
		return ImportNewModule(filePath)
	end
end

function Import(filePath)
	local ok, mod, internalErr = pcall(SafeImport, filePath, false)
	if not ok then
		debug.excepthook(mod)
		error(mod)
	end

	if not mod then
		debug.excepthook(internalErr)
		error(internalErr)
	end

	return mod
end